<?php

// 数据采集: doCurl,doFile, 文件下载，

/*
    Http::setCache(30); // 缓存30分钟
    $opt = [
        'timeout' => 7, 
        'type' => 'html', xml, json, down
        'User-Agent' => '',
        'ref' => 'http://x_yz.com/file.htm', 来路模拟
        'proxy' => ['ip','80'], proxy设置
        'Cookie' => "...", cookie设置
        'gzip' => '',
    ]
*/

class Http
{

    static $cache = 0;
    static $timeout = 7;

    //手动设置缓存
    static function setCache($exp){
        self::$cache = intval($exp) * 60;
    }

    # curl-抓资料
    static function doCurl($url, $post=1, $data=[], $opt=[]){
        // getCache
        $cres = self::getCache($url, $data);
        if(!empty($cres[1])) return $cres[1];
        // init
        $ch = curl_init(); 
        if(!empty($data) && !$post){
            $exurl = http_build_query($data); // xml,json ?
            $url .= (strpos($url,'?')?'&':'?') . $exurl;
            $data = [];
        }
        curl_setopt($ch, CURLOPT_URL, $url); 
        // exp
        $exp = empty($opt['timeout']) ? self::$timeout : $opt['timeout'];
        curl_setopt($ch, CURLOPT_TIMEOUT, $exp);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $exp);
        // proxy
        if(isset($opt['ref'])){ //模拟来源地址
            curl_setopt($ch, CURLOPT_REFERER, $opt['ref']); 
        }
        if(isset($opt['proxy'])){
            $proxy = $opt['proxy'];
            curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); //代理认证模式  
            curl_setopt($ch, CURLOPT_PROXY, $proxy[0]); //代理服务器地址   
            curl_setopt($ch, CURLOPT_PROXYPORT, $proxy[1]); //代理服务器端口
        }
        // header
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $header = self::headArr($opt, $data, 'curl');
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        // post-data
        if($post){
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
            curl_setopt($ch, CURLOPT_POST, true);
            if(!empty($data)){
                curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 
            }
        }
        // gzip
        if(isset($opt['gzip'])){ //模拟来源地址
            $gzip = empty($opt['gzip']) ? 'gzip,deflate' : $opt['gzip'];
            curl_setopt($ch, CURLOPT_ENCODING, $gzip); //curl解压gzip页面内容
        } 
        // https
        if(substr($url,0,8)=='https://'){
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            if(isset($opt['sslv'])){ // 2-7, 默认情况下PHP会自己检测这个值
                curl_setopt($ch, CURLOPT_SSLVERSION, $opt['sslv']);
            } // ['TLSv1_3','TLSv1_2','TLSv1_1','TLSv1_0','SSLv3','SSLv2','SSLv1']
        }
        // saveCache & return
        $result = curl_exec($ch);
        if(!empty($cres[0])) self::saveCache($cres[0], $result);
        curl_close($ch);
        return $result;
    }

    // socket-抓资料 
    static function doSocket($url, $post=1, $data=[], $opt=[]) {
        return false; //一般抓取数据，用curl替代
    }

    // file_get_contents-抓资料
    static function doFile($url, $post=1, $data=[], $opt=[]){
        // getCache
        $cres = self::getCache($url, $data);
        if(!empty($cres[1])) return $cres[1];
        // post
        if(!empty($data) && !$post){
            $exurl = http_build_query($data); // xml,json ?
            $url .= (strpos($url,'?')?'&':'?') . $exurl;
            $data = [];
        }
        // timeout/header
        $exp = empty($opt['timeout']) ? self::$timeout : $opt['timeout'];
        $heads = self::headArr($opt, $data, 'file');
        $header = implode(PHP_EOL, $heads);
        $opts = array( 
            'http'=>array(
                'protocol_version'=>'1.0',//http协议版本(若不指定php5.2系默认为http1.0)
                'timeout' => $exp , 
                'method'=> (empty($post) ? 'GET' : 'POST'),
            )
        ); 
        // https
        if(substr($url,0,8)=='https://'){
            $opts['ssl'] = ["verify_peer"=>false, "verify_peer_name"=>false];
        }
        if(!empty($data)){
            $data = is_array($data) ? http_build_query($data) : $data; 
            $header .= PHP_EOL."Content-length: ".strlen($data);
            $opts['http']['content'] = $data;
        }
        // ref
        if(isset($opt['ref'])){ //模拟来源地址
            $header .= PHP_EOL."Referer: ".$opt['ref'];
        } //dump($in);
        $opts['http']['header'] = $header; //dump($header);
        // do
        $context = stream_context_create($opts);
        $result = file_get_contents($url, false, $context);
        // saveCache
        if(!empty($cres[0])) self::saveCache($cres[0], $result);
        // return
        return $result;
    }
    
    // 组装 head
    static function headArr($opt=[], &$data=[], $func=''){
        $header = []; $ctype = 0;
        $header[] = "User-Agent: " . (empty($opt['User-Agent']) ? uval('HTTP_USER_AGENT','','s') : $opt['User-Agent']);
        if(isset($opt['Cookie'])){ $header[] = "Cookie: ".$opt['Cookie']; } // cookie设置
        $tag = array(
            'html'=>'text/html',
            'xml'=>'text/xml; charset=utf-8', 
            'json'=>'application/json; charset=utf-8',
            'down'=>'application/octet-stream',
        );
        if(isset($opt['type']) && isset($tag[$opt['type']])){ // json
            $header[] = "Content-Type: ".$tag[$opt['type']];
            if($opt['type']=='json'){
                if(is_array($data)) $data = json_encode($data);
            }
            $ctype = 1;
        }
        if(empty($ctype) && $func=='file'){
            $header[] = "Content-Type: application/x-www-form-urlencoded"; 
        } //dump($header);
        return $header;
    }

    # === 缓存处理 =======================================

    // get-缓存
    static function getCache($url, $data=[]) {
        if(!self::$cache || !$url) return [0, 0];
        $file = Fsop::safeName($url); 
        $fp = DIR_DATA."/remote/$file"; 
        if(file_exists($fp)){
            $last = filemtime($fp);
            $data = ($last+self::$cache > $_SERVER["REQUEST_TIME"]) ? file_get_contents($fp) : '';
        }else{
            $data = '';
        }
        return [$fp, $data];
    }
    // save-缓存
    static function saveCache($fp, $data) {
        if(empty($data)) return false;
        $res = file_put_contents($fp, $data);
        return $res;
    }

    # === down =======================================

    // 下载web中的(或URL)文件
    static function downLoad($realname, $showname='', $expire=1800){
        $size = self::downCheck($realname, $showname, 1);
        set_time_limit(0);
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Transfer-Encoding: binary');
        header('Accept-Ranges: bytes');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: ' . $size);
        header('Content-Disposition: attachment; filename=' . $showname);
        # readfile($realname);
        $fp = fopen($realname, 'rb');
        ob_clean();
        ob_end_flush();
        while (!feof($fp)) {
            echo fread($fp, $size);
            ob_flush(); // 刷新PHP缓冲区到Web服务器
            flush(); // 刷新Web服务器缓冲区到浏览器
        }
        fclose($fp);
        return true;
    }
    // 保存远程页面(图片)到本地
    static function downSave($url, $showname='', $curl=0){
        if(!$url) return;
        self::downCheck($url, $showname);
        if($curl){
            $data = self::doCrawl($url);
        }else{
            ob_start();
            readfile($url);
            $data = ob_get_contents();
            ob_end_clean();
        }
        Fsop::put($showname, $data);
        return $showname;
    }
    // downCheck
    static function downCheck(&$url, &$showname, $chkexists=0){
        $fsize = 0;
        if(preg_match('/^https?:\/\//',$url)){
            ini_set('allow_url_fopen', 'On');
        }
        if(empty($url) || ($chkexists && !file_exists($url))) {
            abort("File/Page `$url` Not Exists!", 610);
        }else{
            $fsize = @filesize($url);
        }
        if(empty($showname)){
            $info = parse_url($url);
            $_path = explode("/",$info['path']);
            $showname = end($_path);
        }
        return $fsize;
    }
    
    # === status/dallow =======================================

    // 发送HTTP状态
    static function status($code, $remsg=0) {
        $_status = read('sy_https','xtab') + read('sy_https');
        $exmsg = isset($_status[$code]) ? $_status[$code] : 'Unknow Error';
        if($remsg){ return $exmsg; }
        if(isset($_status[$code])) {    
            header('HTTP/1.1 '.$code.' '.$exmsg);
            header('Status:'.$code.' '.$exmsg); // 确保FastCGI模式下正常
        }
    }

    // domain-跨域允许
    static function dallow($domain=''){
        $domain = $domain ? $domain : '*';
        header("Access-Control-Allow-Origin:$domain"); // http://127.0.0.1, 指定允许其他域名访问
        header('Access-Control-Allow-Credentials:true'); // 允许携带 用户认证凭据（也就是请求携带Cookie）
        header('Access-Control-Allow-Methods:GET,HEAD,OPTIONS,POST,PUT'); // 响应类型  
        header('Access-Control-Allow-Headers:X-Requested-With,Content-Type');
        header('X-Frame-Options:ALLOWALL'); //ALLOWALL，ALLOW-FROM
        // Access-Control-Allow-Headers,Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers
    }

}

